/*********************************************************************************
 *      Copyright:  (C) 2019 Wu Yujun<540726307@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  create_thread.c
 *    Description:  This file create pthread
 *                 
 *        Version:  1.0.0(2019年06月29日)
 *         Author:  Wu Yujun <540726307@qq.com>
 *      ChangeLog:  1, Release initial version on "2019年06月29日 09时39分01秒"
 *                 
 ********************************************************************************/
#include "create_thread.h"
#include "gsmd.h"
#include "server_socket.h"
#include "at_cmd.h"
#include "ppp.h"
#include "gsmd_tlv.h"



/*********************************************************************
 *  描述：
 *      创建gsmd程序需要的线程，几个线程通过传进去的参数进行线程间通信
 *  返回值：
 *      成功返回0，失败返回负数
 * ******************************************************************/
int gprsd_creat_thread(pthread_t * ppp_id, THREAD_BODY * ppp_thread, 
        pthread_t *sendmess_id, THREAD_BODY * sendmess_thread, 
        pthread_t *socket_id, THREAD_BODY * socket_thread,
        void *gsmd_ctx)
{
    int rv  = -1;

    //printf("Running in %s\n",__FUNCTION__) ;

    if(ppp_id == NULL || sendmess_id == NULL || socket_id == NULL)
    {
        printf("Input invail parameter!\n") ;
        return -1 ;
    }
    rv = create_pthread(ppp_id, ppp_thread, gsmd_ctx) ; //创建PPP线程
    if(rv < 0 )
    {
        printf("create ppp_thread error\n") ;
        rv  = -2 ;
        goto cleanup ;
    }
    rv = create_pthread(sendmess_id, sendmess_thread, gsmd_ctx) ; //创建发送短信线程
    if(rv < 0 )
    {
        printf("create sendmess_thread error\n") ;
        rv = -3 ;
        goto cleanup ;
    }
    rv =create_pthread(socket_id, socket_thread, gsmd_ctx) ; //创建socket线程
    if(rv < 0 )
    {
        printf("create socket_thread error\n") ;
        rv  = -4;
        goto cleanup ;
    }

    rv = 0 ;
cleanup:
    return rv ;
}

/* *********************************************************************
 * 描述：创建线程
 *
 * 参数：
 *      pthread_t *thread_id指向类型为pthread_t类型的指针，传参传地址用于返回tid
 *      thread_wrokbody是个函数指针，传入函数地址
 *      thread_arg是传入thread_wrokbody线程处理函数的参数
 *
 *返回值：
 *      成功返回0，失败返回-1；
 * ********************************************************************/
int create_pthread(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg)
{
    int                 rv = -1;
    pthread_attr_t      thread_attr; 


    //printf("Running in %s\n",__FUNCTION__) ;
    if( pthread_attr_init(&thread_attr) )
    {
        printf("pthread_attr_init() failure: %s\n", strerror(errno));
        goto CleanUp;
    } 

    if( pthread_attr_setstacksize(&thread_attr, STACK_SIZE) )
    {
        printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
        goto CleanUp;
    } 

    if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )
    {
        printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
        goto CleanUp;
    } 

    /*  Create the thread */
    if( pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg) )
    {
        printf("Create thread failure: %s\n", strerror(errno));
        goto CleanUp;
    }

    rv = 0;

CleanUp:

    /*  Destroy the  attributes  of  thread */ 
    pthread_attr_destroy(&thread_attr); 
    return rv;
}


/*      ppp拨号上网线程         */
void *ppp_thread(void *ctx)
{
    int                rv = -1 ;
    st_gsmd_ctx        *gsmd_ctx = (st_gsmd_ctx *)ctx;
    if( !gsmd_ctx )
    {
        printf("Invalid input arguments in %s()\n", __FUNCTION__);
        pthread_exit(NULL);
    }
    while(1)
    {
        if(gsmd_ctx->gsmRegInfo.ready != OK)
        {
            sleep(10) ;
            continue ;
        }
        if(gsmd_ctx->worker.current != REQ_PPP && gsmd_ctx->worker.request != REQ_PPP) //当前状态与请求状态都与PPP无关
        {
            sleep(5) ;
            continue ;
        } 
        if(gsmd_ctx->worker.current == REQ_PPP && gsmd_ctx->worker.request <= REQ_PPP) //当前状态为PPP但是没有高优先级请求抢占
        {
            /* Ping 检查网络连通性 */
            if(gsmd_ctx->worker.PPP == ON)
            {
                rv = ping_check_net() ;
                if(rv < 0)
                {
                    printf("ppp disconnet\n") ;
                    goto ppp_conn ;   //不能ping通就去拨号上网
                }
                sleep(10) ;   //能ping通就休眠，下一次循环  
                continue ;
            }
        }
        if(gsmd_ctx->worker.current == REQ_PPP && gsmd_ctx->worker.request > REQ_PPP)
        {
            rv = close_ppp(Data_COM) ; //通过发送ATH0挂断拨号上网
            if(rv < 0)
            {
                printf("close_ppp error\n") ;
                goto check_reg ;    //关闭失败检查注册情况
            }
            gsmd_ctx->worker.status = FREE ;
            pthread_mutex_lock(&(gsmd_ctx->worker.lock));
            gsmd_ctx->worker.request |= REQ_PPP ; //被抢占PPP请求标志位置1
            pthread_mutex_unlock(&(gsmd_ctx->worker.lock));
            sleep(3) ;
            continue ;
        }
        if(gsmd_ctx->worker.request == REQ_PPP ) //如果请求为PPP
        {

            if(gsmd_ctx->worker.status == FREE)
            {
                gsmd_ctx->worker.status = BUSY ;
                if(gsmd_ctx->worker.PPP == ON)
                {
/*  拨号上网    */
ppp_conn:
                    rv = ppp_connect(gsmd_ctx->gsmRegInfo, "/etc/ppp/peers/wcdma","/etc/ppp/peers/wcdma-chat-connect") ; //拨号上网
                    if(rv < 0)
                    {
                        printf("ppp_connect error\n") ;
                        goto check_reg ;        //拨号上网失败，检查sim卡是否注册上
                    }
                    pthread_mutex_lock(&(gsmd_ctx->worker.lock));
                    gsmd_ctx->worker.current = REQ_PPP ;
                    gsmd_ctx->worker.request &= (~REQ_PPP) ;
                    pthread_mutex_unlock(&(gsmd_ctx->worker.lock));
                    sleep(4) ;
                }
            }
            if(gsmd_ctx->worker.current == REQ_PPP && gsmd_ctx->worker.PPP == OFF)
            {
                rv = close_ppp(Data_COM) ; //通过发送ATH0挂断拨号上网
                if(rv < 0)
                {
                    printf("close_ppp error\n") ;
                    goto check_reg ;    //关闭失败检查注册情况
                }
                pthread_mutex_lock(&(gsmd_ctx->worker.lock));
                gsmd_ctx->worker.current = 0 ;
                gsmd_ctx->worker.request &= (~REQ_PPP) ;
                pthread_mutex_unlock(&(gsmd_ctx->worker.lock));
                gsmd_ctx->worker.status = FREE ;
                sleep(3) ;
            }
            continue ;
            
        }//if(gsmd_ctx->worker.request == REQ_PPP )

/*  检查sim卡是否注册上  */
check_reg:
        gsmd_ctx->worker.status = FREE ;
        rv = check_regsiter(Data_COM,gsmd_ctx) ; 
        if(rv < 0) //sim卡没注册上    
        {
            gsmd_ctx->gsmRegInfo.ready = NO ;
            sleep(5) ;
            continue ;
        }
        sleep(1) ;   //拨号失败不是因为sim card没注册上，休眠一秒 
        continue ;

     }//while(1) ;
    pthread_exit(NULL);
}

/*      发送短信线程        */
void *sendmess_thread(void *ctx)
{
    int                rv = -1 ;
    st_gsmd_ctx        *gsmd_ctx = (st_gsmd_ctx *)ctx;
    int                 fail_count = 0 ;
    if( !gsmd_ctx )
    {
        printf("Invalid input arguments in %s()\n", __FUNCTION__);
        pthread_exit(NULL);
    }
    while(1)
    {
        if(gsmd_ctx->gsmRegInfo.ready != OK)
        {
            sleep(10) ;
            continue ;
        }
        if(gsmd_ctx->worker.request<REQ_SMS || gsmd_ctx->worker.status != FREE)
        {
            sleep(5) ;
            continue ;
        }
        gsmd_ctx->worker.status = BUSY ;
            
        rv = send_mess(Control_COM,gsmd_ctx->worker.tel_num,gsmd_ctx->worker.mess) ;
        if(rv<0)
        {
            gsmd_ctx->worker.status = FREE ;
            printf("Send message ERROR\n") ;
            rv = check_regsiter(Data_COM,gsmd_ctx) ; 
            if(rv < 0)    //发送短信失败因为手机卡没注册上
            {
                gsmd_ctx->gsmRegInfo.ready = NO ;
                sleep(5) ;
                continue ;
            }
            sleep(1) ;   //发送短信失败但不是手机卡没注册上
            fail_count++ ;
            /* 发送失败三次，但是却不是因为手机卡没注册上，可能是号码错误或者短信内容包含不可发送的字符 */
            if(fail_count == 3)
            {
                printf("Please check telephone number or message is true or not?\n") ;
                fail_count  = 0 ;
                pthread_mutex_lock(&(gsmd_ctx->worker.lock));
                gsmd_ctx->worker.request &=( ~REQ_SMS );
                pthread_mutex_unlock(&(gsmd_ctx->worker.lock));
                gsmd_ctx->worker.status = FREE ;
            }
            continue ;
        }
        /*  Send message sucessful    */
        pthread_mutex_lock(&(gsmd_ctx->worker.lock));
        gsmd_ctx->worker.current = REQ_SMS ;
        gsmd_ctx->worker.request &=( ~REQ_SMS );
        pthread_mutex_unlock(&(gsmd_ctx->worker.lock));
        gsmd_ctx->worker.status = FREE ;
        sleep(3) ;
    }
    pthread_exit(NULL);
}

/*      socket 线程     */
void *socket_thread(void *ctx) 
{
    int                 cli_fd = -1 ;
    st_gsmd_ctx        *gsmd_ctx = (st_gsmd_ctx *)ctx;
    int                 rv = -1 ;
    char                true_tlv[BUFSIZE] ;
    int                 true_size ;
    char                logon_buf[BUFSIZE] ;
    int                 logon_bytes = 0 ;
    int                 bytes = 0 ;
    char                buf[BUFSIZE] ;
    fd_set              rdset ;
    struct timeval      timeout; 
    char                sim_info[64] ;


    if( !gsmd_ctx )
    {
        printf("Invalid input arguments in %s()\n", __FUNCTION__);
        pthread_exit(NULL);
    }
    gsmd_ctx->gsmSocket.fd = creat_socket(gsmd_ctx->gsmSocket.lis_addr, gsmd_ctx->gsmSocket.port) ;
    if(gsmd_ctx->gsmSocket.fd < 0)
    {
        printf("creat_socket error") ;
        goto cleanup ;
    }

    while(1)
    {
        /*       接收新的客户端   */
        if(cli_fd < 0)
        {
            /*  登录，需要发送正确密码才能登陆并控制模块 */
            true_size = 0 ;
            memset(true_tlv, 0 ,sizeof(true_tlv)) ;
            printf("Have client want to connect!\n") ;
            cli_fd = accept_client(gsmd_ctx->gsmSocket.fd) ;
            if( cli_fd < 0)
            {
                printf("Accept error\n") ;
                continue ;
            }
            /*        If 3 seconds have on reply, will close client   */
            if((rv =  logon_rev(cli_fd, &logon_buf[logon_bytes], BUFSIZE-logon_bytes)) < 0) 
            {
                printf("Logon reply error,and close client\n") ;
                close(cli_fd) ;
                cli_fd = -1 ;
                continue ;
            }
            logon_bytes = rv + logon_bytes ;
            logon_bytes = unpack_tlv(logon_buf,logon_bytes, true_tlv, &true_size) ;
            /*        If passwd is true EPOLL_CTL_ADD, else close client      */
            if( tlv_logon(true_tlv,true_size) != LOGON_OK)
            {
                close(cli_fd) ;
                cli_fd = -1 ;
                continue ;
            }
            printf("\nLOGON OK!!!!!\n") ;
            gsmd_ctx->gsmSocket.cli_fd = cli_fd ;
        }

        FD_ZERO(&rdset);
        FD_SET(cli_fd, &rdset);
        
        timeout.tv_sec = 20;
        timeout.tv_usec = 0; 

        rv = select(gsmd_ctx->gsmSocket.cli_fd +1, &rdset, NULL, NULL, &timeout); 
        if(  rv < 0 )
        {
            printf("Select get timeout or error: %s\n", strerror(errno));
            continue;
        }
        if(rv == 0) //如果20秒钟客户端都没有发送请求，超时，发送sim信息给客户端
        {
            if(gsmd_ctx->gsmRegInfo.ready == OK)
            {
                memset(sim_info, 0 , sizeof(sim_info)) ;
                packtlv_signal(sim_info, sizeof(sim_info), gsmd_ctx->gsmRegInfo.sim_signal) ;
                rv = write(gsmd_ctx->gsmSocket.cli_fd, sim_info, strlen(sim_info)) ;
                if(rv < 0)
                {
                    printf("Write to client error, close client and accept new client") ;
                    close(cli_fd) ;
                    cli_fd = -1 ;
                    gsmd_ctx->gsmSocket.cli_fd = -1 ;
                    continue ;
                }
                continue ;
            }
        }
        true_size = 0 ;
        memset(true_tlv, 0 ,sizeof(true_tlv)) ;
        rv = read(gsmd_ctx->gsmSocket.cli_fd , &buf[bytes], BUFSIZE - bytes);
        if(rv <= 0)
        {
            printf("Read() error:%s\n", strerror(errno)) ;
            close(cli_fd) ;
            cli_fd = -1 ;
            gsmd_ctx->gsmSocket.cli_fd = -1 ;
            continue;
        }
        bytes = rv + bytes ;
        bytes = unpack_tlv(buf,bytes, true_tlv, &true_size) ;
        if(true_size == 0)
        {
            printf("\nERROR CMD\n") ;
            continue ;
        }
        print_tlv("TRUE_TLV",true_tlv, true_size) ;

        if(tlv_gprs(gsmd_ctx->gsmSocket.cli_fd , true_tlv,true_size, gsmd_ctx) < 0)
        {
            printf("tlv_elora() failed\n") ;
            close(cli_fd) ;
            cli_fd = -1 ;
            gsmd_ctx->gsmSocket.cli_fd = -1 ;
            continue ;
        }
    }

cleanup:
    pthread_exit(NULL);
}
